#!/bin/bash
## (c) Denis Kaganovich
## v5.7

: ${TMPDIR:=/tmp}

# grep 2.15 utf8
export LANG=C

#BUS="PCI"
BUS="\w+"
STR="(?:^(?:static\s+)?(?:const\s+)?struct\s+${BUS,,}_device_id(?:\s+\w+)+|^(?:static\s+)?DEFINE_${BUS}_DEVICE_TABLE\(.*?\))"

hdr(){
	local h="$2" i x
	h="${h#$1}"
	h="${h#/}"
	for i in $(grep -sPrhl "^\s*#include\s+[\"<](?:.*/)?${h##*/}[\">]" $1 --include "*.[ch]"); do #"
		x="${i#$1}"
		x="${x#/}"
		x="${x%/*}"
		( [[ -z ${h##$x/*} ]] && grep -qP "^\s*#include\s+\"${h#$x/}\"" "$i" ) ||
		 ( [[ -z "${h##include/*}" ]] && grep -qP "^\s*#include\s+<${h#include/}>" "$i" ) ||
		 continue
		case "$i" in
		*.h)hdr $1 "$i";;
		*)echo "$i";;
		esac
	done
}

str(){
local bus b i=$1 h=$2 m=
grep -q "MODULE_DEVICE_TABLE" $i $h && return
b=`grep -Prho "$STR" $i` && h=$i || bus=`grep -Prho "$STR" $h`
# ???
while [ -n "$b" ]; do
	bus="${b%%
*}"
	[ "$bus" = "$b" ] && b='' || m='+'
	b="${b#*
}"
	case "$bus" in
	*DEFINE_*_DEVICE_TABLE*)
		bus="${bus#*DEFINE_}"
		n="${bus%%)*}"
		n="${n##*(}"
		bus="${bus%%_*}"
		[[ "$bus" != PCI ]] && echo "Debug: DEFINE_$bus_*DEVICE_TABLE in $i" >&2
		bus="${bus,,}"
	;;
	*)
		bus="${bus#*struct }"
		n="${bus%%[\[=]*}"
		n="${n##*device_id }"
		bus="${bus%%_device_id*}"
	;;
	esac
	while [ -z "${n%%* }" -a -n "$n" ]; do
		n="${n%% }"
	done
	n="${n##* }"
	echo "$i $h $bus $n $m"
done
}

fnd(){
local i h bus n m
grep -sPrhl "$STR" $1 --include "*.[hc]"|while read h; do
	case "$h" in
	*.h)for i in $(hdr $1 "$h"); do
		str $i $h
	done;;
	*)str $h $h;;
	esac
done|while read i h bus n m; do
#grep -Prhl "$STR" $1 --include="*.c" | while read i ; do h:="$i"
	grep -q "^module_init[ 	]*(\|^MODULE_DESCRIPTION(" $i || continue
	grep -q "MODULE_DEVICE_TABLE.*$n" $i $h && continue
#	[ -z "${bus_##*
#*}" ] && echo "TODO: multi-bus device: $i - ${bus_}" && continue
	local ii="${i#$1}"
	ii="${ii#/}"
	# really it can be in .h, but too sometimes
	grep -sqP "^\s*\#include\s+<linux/module\.h>" "$i" && mh=true || mh=false
	if [[ "$2" != "f" ]] ; then
		local b="$bus"
		case $b in
			of) b="of_platform";;
		esac
		grep -Pq "${STR}${b}_driver\s+" $i || {
			echo "UNSURE: $bus: $ii"
			continue
		}
		$mh || {
			echo "UNSURE: $bus: $ii - no linux/module.h"
			continue
		}
	fi
	bus="${bus%%_*}"
#	case "$bus:$ii" in
#	*/neo1973_wm8753.c)
#		echo "SKIPPED: $bus: $ii"
#		continue
#	;;
#	esac
	local BU
	case "$bus" in
	sdio)BU=MMC;;
	rio)BU=RAPIDIO;;
	*)BU="${bus^^}";;
	esac
	if [[ "$2" != "f" ]] && ! grep -Pqr "MODULE_DEVICE_TABLE\($bus," $1 --include="*.c" ; then
		echo "UNKNOWN BUS: $bus ($ii)"
		continue
	fi
	add=''
	[[ "$i" == "$h" ]] && add='
/* _module_id_ */
'
	export add n
	if perl -e 'sub fix{
			my ($s0,$s)=(@_);
			my $s1=$s;
			my $s2;
			($s=~s/.*\{//gs)||return(($s0=~/[\]\)]/)?$s0.$s1:"[]$s0\{$s1\}, \{\}");
			$s0.=$s1;
			$s1=$s0;
			$s=~s/\/\*.*\*\///gs;
			$s=~s/,\s*$//s || ($s1=~s/\}/\},/s) || exit 1;
			$s=~s/\"\"//gs;
			$s=~s/\s//gs;
			$s=~s/\}$//s || exit 1;
			$s=~s/[0,]//gs;
			$s=~s/\.\w+=//gs;
#			print STDERR "FIX0:";
			$s || return $s0;
			$s1.="\n	\{\}\n";
			$s1
		}
		while(<STDIN>){$s.=$_}
	exit !($s=~s/('"${STR//^/\\n}"'[^=;\{\}\]\)]*)([\]\)]?\s*=\s*\{)([^;]*)(\s*?\};)/index($1,$ENV{n})>=0?$1.fix("$2","$3").$4.$ENV{add}:$1.$2.$3.$4/ges && print $s)' <$h >"$TMPDIR/modulesfix.tmp" ; then
		cmp -s "$TMPDIR/modulesfix.tmp" $h && rm "$TMPDIR/modulesfix.tmp" || {
			[[ "$i" != "$h" ]] && echo "Fixing: ($n) ${h#$1/}"
			mv "$TMPDIR/modulesfix.tmp" $h
		}
	elif [[ "$2" != "f" ]]; then
		echo "BROKEN/UNSURE: $bus: $ii"
		continue
	fi
	xx='\nMODULE_DEVICE_TABLE('"$bus, $n"');'
	$mh || xx='\n#include <linux/module.h>'"$xx"
	if grep -Prq "^\s*(?:menu)?config\s+$BU(?:\s.*)?$" $1 --include="Kconfig*"; then
		echo "Fixing$m: $BU: ($n) $ii"
		xx='\n#ifdef CONFIG_'"$BU$xx"'\n#endif'
	else
		echo "Fixing$m: $bus: ($n) $ii"
	fi
	sed -i -e 's%^/\* _module_id_ \*/$%'"$xx"'\n%' $i
	grep -q "^MODULE_DEVICE_TABLE" $i || echo -e "$xx" >>$i
done
}

[[ -z "$1" ]] && echo "$0 <path_to_kernel> [f]" && exit 1

fnd $*
